#include <SoftwareSerial.h>
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#define PIN        8
#define NUMPIXELS  8 // Popular NeoPixel ring size
#define DELAYVAL 80
SoftwareSerial mySerial(11, 10); // RX, TX
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int address = 1, addressA = 2, addressV = 3, addressW = 4, addressR = 6;

const char BEEP_PIN = 9;
const char CD4052_INH_A = 3;
const char CD4052_A_A = 4;
const char CD4052_A_B = 5;

const char CD4052_INH_B = 2;
const char CD4052_B_A = 7;
const char CD4052_B_B = 6;

String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete

unsigned char Read_ID = 0x01;
unsigned char Tx_Buffer[8];
unsigned char Rx_Buffer[40];
float _data_set[24];
int _limmitValueA;
int _limmitValueV;
int _limmitValueW;
int _brightness = 45;
unsigned long timerStart;
int index = 1;
boolean SerialCmd = true;
unsigned char read_enable, receive_finished, reveive_number = 29;
unsigned long Voltage_data, Current_data, Power_data, Energy_data, Pf_data, CO2_data;

void(* resetFunc) (void) = 0;
void writeIntIntoEEPROM(int address, int number)
{
  byte byte1 = number >> 8;
  byte byte2 = number & 0xFF;
  EEPROM.write(address, byte1);
  EEPROM.write(address + 1, byte2);
}
int readIntFromEEPROM(int address)
{
  return (EEPROM.read(address) << 8) + EEPROM.read(address + 1);
}
void _beep_last_time(int _time)
{
  noTone(BEEP_PIN);
  tone(BEEP_PIN, 440, 200);
  for (int i = 0; i < NUMPIXELS; i++)
  {
    pixels.setPixelColor(i, pixels.Color(0, _brightness, 0));
    pixels.show();   // Send the updated pixel colors to the hardware.
    if (i == NUMPIXELS / 2 - 1)
      tone(BEEP_PIN, 523, 300);
    delay(_time); // Pause before next pass through loop
  }
  noTone(BEEP_PIN);
}
void _beep(int _time)
{
  noTone(BEEP_PIN);
  tone(BEEP_PIN, 440, 200);
  delay(_time / 2); // Pause before next pass through loop
  tone(BEEP_PIN, 523, 300);
  delay(_time / 2); // Pause before next pass through loop
  noTone(BEEP_PIN);

}
void en_cd4052(char phase)
{
  if (phase == 'A')
  {
    digitalWrite(CD4052_INH_A, LOW);
    digitalWrite(CD4052_INH_B, HIGH);
  }
  if (phase == 'B')
  {
    digitalWrite(CD4052_INH_B, LOW);
    digitalWrite(CD4052_INH_A, HIGH);
  }
}
void enable_TRX(int chanel)
{
  switch (chanel)
  {
    case 1:
      {
        en_cd4052('A');
        digitalWrite(CD4052_A_A, LOW);
        digitalWrite(CD4052_A_B, LOW);
      }
      break;
    case 2:
      {
        en_cd4052('A');
        digitalWrite(CD4052_A_A, HIGH);
        digitalWrite(CD4052_A_B, LOW);
      }
      break;
    case 3:
      {
        en_cd4052('A');
        digitalWrite(CD4052_A_A, LOW);
        digitalWrite(CD4052_A_B, HIGH);
      }
      break;
    case 4:
      {
        en_cd4052('A');
        digitalWrite(CD4052_A_A, HIGH);
        digitalWrite(CD4052_A_B, HIGH);
      }
      break;
    case 5:
      {
        en_cd4052('B');
        digitalWrite(CD4052_B_A, LOW);
        digitalWrite(CD4052_B_B, LOW);
      }
      break;
    case 6:
      {
        en_cd4052('B');
        digitalWrite(CD4052_B_A, HIGH);
        digitalWrite(CD4052_B_B, LOW);
      }
      break;
    case 7:
      {
        en_cd4052('B');
        digitalWrite(CD4052_B_A, LOW);
        digitalWrite(CD4052_B_B, HIGH);
      }
      break;
    case 8:
      {
        en_cd4052('B');
        digitalWrite(CD4052_B_A, HIGH);
        digitalWrite(CD4052_B_B, HIGH);
      }
      break;
  }
}
void setup() {
  // put your setup code here, to run once:
  _brightness = EEPROM.read(address);
  _limmitValueA = EEPROM.read(addressA);
  _limmitValueV = EEPROM.read(addressV);
  _limmitValueW = readIntFromEEPROM(addressW);
  SerialCmd = EEPROM.read(addressR);
  Serial.begin(115200);
  mySerial.begin(4800);
  pixels.begin();
  pixels.clear();
  //  Serial.println("Hello, world?");
  pinMode(BEEP_PIN, OUTPUT);
  pinMode(CD4052_INH_A, OUTPUT);
  pinMode(CD4052_INH_B, OUTPUT);
  pinMode(CD4052_A_A, OUTPUT);
  pinMode(CD4052_A_B, OUTPUT);
  pinMode(CD4052_B_A, OUTPUT);
  pinMode(CD4052_B_B, OUTPUT);
  digitalWrite(CD4052_INH_A, HIGH);
  digitalWrite(CD4052_INH_B, HIGH);
  digitalWrite(BEEP_PIN, LOW);
  _beep_last_time(100);
  inputString.reserve(200);


}

void loop() {
  // put your main code here, to run repeatedly:
  if (SerialCmd)
  {
    read_data();
    if (mySerial.available()) {
      reveive_number = mySerial.readBytes(Rx_Buffer, 29);
      Analysis_data();
    }
  }
  //cmd
  if (stringComplete) {
    if (inputString[0] == 'H')
    {
      Serial.println();
      Serial.println("-----This is Help List-----");
      Serial.println("-You can Set the Limit Value");
      Serial.println("*Current Limit,Watt Limit,Voltage Limit*");
      Serial.println();
      Serial.println("Now Values:");
      Serial.print("    ");
      Serial.print(" limmitValueA:");
      Serial.println(_limmitValueA);
      Serial.print("    ");
      Serial.print(" limmitValueW:");
      Serial.println(_limmitValueW);
      Serial.print("    ");
      Serial.print(" limmitValueV:");
      Serial.println(_limmitValueV);
      Serial.print("    ");
      Serial.print(" Brightness:");
      Serial.println(_brightness);
      Serial.println("");
      Serial.println("-Such as Send Message to Serial:A = 25   (no Space)");
      Serial.println("-Such as Send Message to Serial:W = 600  (no Space)");
      Serial.println("-Such as Send Message to Serial:V = 25   (no Space)");
      Serial.println("-Such as Send Message to Serial:B = 99   (no Space)");
      Serial.println();
      Serial.println("These CMDs can change limit Value,effect after reboot");
      Serial.println();
      Serial.println("Reboot Dev :Send Message to Serial:R = 1");
      SerialCmd = false;
      Serial.println("");
      Serial.println("Then Enjoy");
    }
    if (inputString[0] == 'F' && inputString[1] == 'u' && inputString[2] == 'c')
    {
      Serial.println("Call Me while confused:18123939181");
    }
    if (inputString[0] == 'A' && inputString[1] == '=')
    {
      int ten = inputString[2] - '0';
      int num = inputString[3] - '0';
      Serial.print("Limit A has benn set to: ");
      Serial.print(ten * 10 + num);
      Serial.println("A");
      EEPROM.write(addressA, ten * 10 + num);
    }
    if (inputString[0] == 'V' && inputString[1] == '=')
    {
      int ten = inputString[2] - '0';
      int num = inputString[3] - '0';
      Serial.print("Limit V has benn set to: ");
      Serial.print(ten * 10 + num);
      Serial.println("V");
      EEPROM.write(addressV, ten * 10 + num);
    }
    if (inputString[0] == 'W' && inputString[1] == '=')
    {
      int han = inputString[2] - '0';
      int ten = inputString[3] - '0';
      int num = inputString[4] - '0';
      Serial.print("Limit W has benn set to: ");
      Serial.print(han * 100 + ten * 10 + num);
      Serial.println("W");
      writeIntIntoEEPROM(addressW, han * 100 + ten * 10 + num);
    }
    if (inputString[0] == 'B' && inputString[1] == '=')
    {
      int ten = inputString[2] - '0';
      int num = inputString[3] - '0';
      Serial.print("Limit Brightness has benn set to: ");
      Serial.print(ten * 10 + num);
      Serial.println(" level");
      EEPROM.write(address, ten * 10 + num);
    }
    if (inputString[0] == 'R' && inputString[1] == '=')
    {
      int reset_cmd = inputString[2] - '0';
      Serial.print("Resting......");
      reset_cmd = 1;
      EEPROM.write(addressR, reset_cmd);
      resetFunc();
    }
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
  if (receive_finished == 0)
  {
    enable_TRX(index++);
    read_enable = 1;
    receive_finished = 1;
    timerStart = millis();
    if (index > 8)
    {
      index = 1;
    }
  }
}
void serialEvent() {
  while (Serial.available()) {

    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}
unsigned int calccrc(unsigned char crcbuf, unsigned int crc)
{
  unsigned char i;
  unsigned char chk;
  crc = crc ^ crcbuf;
  for (i = 0; i < 8; i++)
  {
    chk = ( unsigned char)(crc & 1);
    crc = crc >> 1;
    crc = crc & 0x7fff;
    if (chk == 1)
      crc = crc ^ 0xa001;
    crc = crc & 0xffff;
  }
  return crc;
}
unsigned int chkcrc(unsigned char *buf, unsigned char len)
{
  unsigned char hi, lo;
  unsigned int i;
  unsigned int crc;
  crc = 0xFFFF;
  for (i = 0; i < len; i++)
  {
    crc = calccrc(*buf, crc);
    buf++;
  }
  hi = ( unsigned char)(crc % 256);
  lo = ( unsigned char)(crc / 256);
  crc = (((unsigned int)(hi)) << 8) | lo;
  return crc;
}
void read_data(void)
{
  union crcdata
  {
    unsigned int word16;
    unsigned char byte[2];
  } crcnow;
  if (read_enable == 1) // 到时间抄读模块，抄读间隔 1 秒钟(或其他)
  {
    read_enable = 0;
    Tx_Buffer[0] = Read_ID; //模块的 ID 号，默认 ID 为 0x01
    Tx_Buffer[1] = 0x03;
    Tx_Buffer[2] = 0x00;
    Tx_Buffer[3] = 0x48;
    Tx_Buffer[4] = 0x00;
    Tx_Buffer[5] = 0x06;
    crcnow.word16 = chkcrc(Tx_Buffer, 6);
    Tx_Buffer[6] = crcnow.byte[1]; //CRC 效验低字节在前
    Tx_Buffer[7] = crcnow.byte[0];
    mySerial.write(Tx_Buffer, 8); //发送 8 个数据，请根据单片机类型自己编程
  }
  read_enable = 0;
}
void Analysis_data(void)
{
  unsigned char i;
  union crcdata
  {
    unsigned int word16;
    unsigned char byte[2];
  } crcnow;
  if (receive_finished == 1) //接收完成
  {
    receive_finished = 0;
    if (Rx_Buffer[0] == Read_ID) //确认 ID 正确
    {
      crcnow.word16 = chkcrc(Rx_Buffer, reveive_number - 2); //reveive_numbe 是接收数据总长度
      if ((crcnow.byte[0] == Rx_Buffer[reveive_number - 1]) && (crcnow.byte[1] == Rx_Buffer[reveive_number - 2]))
        //确认 CRC 校验正确
      {
        Voltage_data = (((unsigned long)(Rx_Buffer[3])) << 24) | (((unsigned
                       long)(Rx_Buffer[4])) << 16) | (((unsigned long)(Rx_Buffer[5])) << 8) | Rx_Buffer[6];
        Current_data = (((unsigned long)(Rx_Buffer[7])) << 24) | (((unsigned
                       long)(Rx_Buffer[8])) << 16) | (((unsigned long)(Rx_Buffer[9])) << 8) | Rx_Buffer[10];
        Power_data = (((unsigned long)(Rx_Buffer[11])) << 24) | (((unsigned
                     long)(Rx_Buffer[12])) << 16) | (((unsigned long)(Rx_Buffer[13])) << 8) | Rx_Buffer[14];
        Energy_data = (((unsigned long)(Rx_Buffer[15])) << 24) | (((unsigned
                      long)(Rx_Buffer[16])) << 16) | (((unsigned long)(Rx_Buffer[17])) << 8) | Rx_Buffer[18];
        Pf_data = (((unsigned long)(Rx_Buffer[19])) << 24) | (((unsigned
                  long)(Rx_Buffer[20])) << 16) | (((unsigned long)(Rx_Buffer[21])) << 8) | Rx_Buffer[22];
        CO2_data = (((unsigned long)(Rx_Buffer[23])) << 24) | (((unsigned
                   long)(Rx_Buffer[24])) << 16) | (((unsigned long)(Rx_Buffer[25])) << 8) | Rx_Buffer[26];
        //        Serial.print(index);
        //        Serial.print(",");
        //        Serial.print(Voltage_data);
        //        Serial.print(",");
        //        Serial.print(Current_data);
        //        Serial.print(",");
        //        Serial.print(Power_data);
        //        //        Serial.print(",");
        //        //        Serial.print(Energy_data);
        //        //        Serial.print(",");
        //        //        Serial.print(Pf_data);
        //        //        Serial.print(",");
        //        //        Serial.print(CO2_data);
        //        Serial.println();
        //        Serial.println(index);
        //        Serial.print(Current_data);
        //        Serial.print(";;;");
        //        Serial.println(_limmitValueA);
        if (Current_data / 10000.f > _limmitValueA || Voltage_data / 10000.f > _limmitValueV || Power_data / 10000.f > _limmitValueW)
        {
          int led_order;
          if (index >= 5)
          {
            led_order = index - 5;
          }
          else
          {
            led_order = index + 3;
          }
          pixels.setPixelColor(led_order - 1, pixels.Color(_brightness, 0, 0));
          pixels.show();
          while (1)
          {
            _beep(500);
          }
        }
        float _data[3] = {Voltage_data / 10000.0f, Current_data / 10000.0f, Power_data / 10000.0f};
        memcpy(_data_set + (index - 1) * 3, _data, sizeof(_data));

        if (index == 8)
        {
          Serial.print("/*");
          for (int x = 0; x < 24; x++)
          {

            Serial.print(_data_set[x]);
            if (x < 23)
              Serial.print(",");
            else
              Serial.print("*/");
            //              Serial.println();
          }
          memcpy(_data_set, 0, 23);
        }
      }
    }
  }
  receive_finished = 0;
}
